package com.os.opencv.java.chapter11;

import org.opencv.calib3d.Calib3d;
import org.opencv.core.*;
import org.opencv.features2d.DescriptorMatcher;
import org.opencv.features2d.Features2d;
import org.opencv.features2d.SIFT;
import org.opencv.highgui.HighGui;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;

import java.util.ArrayList;
import java.util.List;

public class RANSAC {

    public static void main(String[] args) {
        System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
        //读取图像并在屏幕上显示
        Mat img1 = Imgcodecs.imread("pics/face.jpg");
        Imgproc.cvtColor(img1, img1, Imgproc.COLOR_BGR2GRAY);
        HighGui.imshow("img1", img1);
        HighGui.waitKey(0);

        //读取图像2并在屏幕上显示
        Mat img2 = Imgcodecs.imread("pics/face.jpg");
        Imgproc.cvtColor(img2, img2, Imgproc.COLOR_BGR2GRAY);
        HighGui.imshow("img2", img2);
        HighGui.waitKey(0);

        //用SIFT算法检测图像中的关键点
        SIFT detector = SIFT.create();
        MatOfKeyPoint kp1 = new MatOfKeyPoint();
        MatOfKeyPoint kp2 = new MatOfKeyPoint();
        Mat des1 = new Mat();
        Mat des2 = new Mat();
        detector.detectAndCompute(img1, new Mat(), kp1, des1);
        detector.detectAndCompute(img2, new Mat(), kp2, des2);

        //创建descriptorMatcher对象并进行knn匹配
        DescriptorMatcher matcher = DescriptorMatcher.create(DescriptorMatcher.FLANNBASED);
        List<MatOfDMatch> km = new ArrayList<>();
        matcher.knnMatch(des1, des2, km, 2);

        //用lowe比率测试筛选匹配项
        float ratio = 0.75f;
        List<DMatch> goodOnes = new ArrayList<>();
        for(int i=0; i<km.size(); i++){
            if(km.get(i).rows() > 1){
                DMatch[] matches = km.get(i).toArray();
                if(matches[0].distance < ratio * matches[1].distance){
                    goodOnes.add(matches[0]);
                }
            }
        }
        MatOfDMatch goodMat = new MatOfDMatch();
        goodMat.fromList(goodOnes);

        //绘制匹配项
        Mat dst = new Mat();
        Features2d.drawMatches(img1, kp1, img2, kp2, goodMat, dst, Scalar.all(-1), Scalar.all(-1), new MatOfByte());

        //获取匹配的特征点
        List<Point> pt1 = new ArrayList<>();
        List<Point> pt2 = new ArrayList<>();
        List<KeyPoint> kpList1 = kp1.toList();
        List<KeyPoint> kpList2 = kp2.toList();

        for(int i=0; i<goodOnes.size(); i++){
            pt1.add(kpList1.get(goodOnes.get(i).queryIdx).pt);
            pt2.add(kpList2.get(goodOnes.get(i).trainIdx).pt);
        }

        //用RANSAC算法筛选匹配结果
        MatOfPoint2f mat1 = new MatOfPoint2f();
        MatOfPoint2f mat2 = new MatOfPoint2f();
        mat1.fromList(pt1);
        mat2.fromList(pt2);
        Mat m = Calib3d.findHomography(mat1, mat2, Calib3d.RANSAC, 3.0);

        //img1的4个顶点
        Mat corners1 = new Mat(4, 1, CvType.CV_32FC2);
        Mat corners2 = new Mat();
        float[] d1 = new float[8];
        int rows = img1.rows();
        int cols = img1.cols();

        d1[0] = 0;
        d1[1] = 0;
        d1[2] = cols;
        d1[3] = 0;
        d1[4] = cols;
        d1[5] = rows;
        d1[6] = 0;
        d1[7] = rows;

        corners1.put(0,0, d1);

        //img2中对应的顶点
        Core.perspectiveTransform(corners1, corners2, m);
        float[] d2 = new float[8];
        corners2.get(0,0,d2);

        //绘制透视变换后的区域
        Scalar color = new Scalar(0,0,255);
        Imgproc.line(dst, new Point(d2[0] + cols, d2[1]), new Point(d2[2] + cols, d2[3]), color, 3);
        Imgproc.line(dst, new Point(d2[2] + cols, d2[3]), new Point(d2[4] + cols, d2[5]), color, 3);
        Imgproc.line(dst, new Point(d2[4] + cols, d2[5]), new Point(d2[6] + cols, d2[7]), color, 3);
        Imgproc.line(dst, new Point(d2[6] + cols, d2[7]), new Point(d2[0] + cols, d2[1]), color, 3);

        //在屏幕上显示会有匹配角点连线的图像
        HighGui.imshow("matches", dst);
        HighGui.waitKey(0);

        System.exit(0);
    }
}
